{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Basics with Numpy (optional assignment)\n",
    "\n",
    "欢迎来到第一个任务。本练习提供了对Python的简要介绍。即使您以前使用过Python，这也可以帮助您熟悉我们即将用到的的功能。\n",
    "\n",
    "**说明：**\n",
    "\n",
    "- 你将学会使用Python 3。\n",
    "- 除非明确告知，否则避免使用for循环和while循环\n",
    "- 不要修改单元格中的（＃GRADED FUNCTION [函数名称]）注释。如果改变，函数会失效。每个包含该注释的单元格仅包含一个函数\n",
    "- 编写完函数后，运行它下面的单元格来检查结果是否正确。\n",
    "\n",
    "**完成这项任务后，您将：**\n",
    "\n",
    "- 能够使用 iPython Notebooks\n",
    "- 能够使用numpy函数和numpy矩阵/矢量操作\n",
    "- 了解python “广播”的概念\n",
    "- 能够矢量化代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## About iPython Notebooks ##\n",
    "\n",
    "iPython Notebooks 是嵌入在网页中的交互式编码环境。您将在本课程中使用iPython Notebooks。您只需要在 ### START CODE HERE ### 和 ### END CODE HERE ### 注释之间编写代码。编写完后，可以通过按“SHIFT”+“ENTER”或单击Notebooks上方的“Run Cell”（用播放符号表示）来运行单元格。\n",
    "\n",
    "我们经常在注释中指定\"(≈ X lines of code)\"，告诉你需要写多少代码。这只是一个粗略的估计，所以如果你的代码更长或更短无需奇怪。\n",
    "\n",
    "**练习**：在下面的单元格中输入“Hello World”并运行下面的两个单元格。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "### START CODE HERE ### (≈ 1 line of code)\n",
    "test = \"Hello World\"\n",
    "### END CODE HERE ###"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test: Hello World\n"
     ]
    }
   ],
   "source": [
    "print (\"test: \" + test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected output**:\n",
    "test: Hello World"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='blue'>\n",
    "**你需要记住的是：**\n",
    "- 用 SHIFT + ENTER（或 \"Run cell\"）运行您的单元格\n",
    "- 仅使用 Python 3 在指定区域编写代码\n",
    "- 不要修改指定区域以外的代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 - Building basic functions with numpy ##\n",
    "\n",
    "Numpy是Python科学计算中的主要软件包。它由一个大型社区维护（www.numpy.org）。在本练习中，您将学习 np.exp，np.log 和 np.reshape 等几个重要的 numpy 函数。因为未来的作业需要使用它们。\n",
    "\n",
    "### 1.1 - sigmoid function, np.exp() ###\n",
    "\n",
    "在使用 np.exp（）之前，您将使用 math.exp（）来实现 sigmoid 函数。然后你会明白为什么np.exp（）比math.exp（）更好一些。\n",
    "\n",
    "**练习**：构建一个返回实数x的sigmoid函数，并使用math.exp（x）作为指数函数。\n",
    "\n",
    "**提示**:\n",
    "$sigmoid(x) = \\frac{1}{1+e^{-x}}$有时也称为 logistic function。它是一种非线性函数，不仅用于机器学习（Logistic回归），还用于深度学习。\n",
    "\n",
    "要引用特定包的函数，可以通过“包名.函数名”的方式来调用。运行下面的代码查看 math.exp（）示例。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: basic_sigmoid\n",
    "\n",
    "import math\n",
    "\n",
    "def basic_sigmoid(x):\n",
    "    \"\"\"\n",
    "    Compute sigmoid of x.\n",
    "\n",
    "    Arguments:\n",
    "    x -- A scalar\n",
    "\n",
    "    Return:\n",
    "    s -- sigmoid(x)\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 1 line of code)\n",
    "    s = 1/(1 + math.exp(-x))\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9525741268224334"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "basic_sigmoid(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "<table style = \"width:40%\">\n",
    "    <tr>\n",
    "    <td>** basic_sigmoid(3) **</td> \n",
    "        <td>0.9525741268224334 </td> \n",
    "    </tr>\n",
    "\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "实际上，我们很少使用“math”库来进行深度学习，因为它的输入都是实数。在深度学习中，我们大多使用矩阵和向量。这也是为什么numpy更有用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type for unary -: 'list'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-16-27db694a2a8a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[1;31m# 当你运行它给出一个错误，因为X是一个矢量\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mbasic_sigmoid\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-3-0bf050bfab2c>\u001b[0m in \u001b[0;36mbasic_sigmoid\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m     15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m     16\u001b[0m     \u001b[1;31m### START CODE HERE ### (≈ 1 line of code)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m     \u001b[0ms\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mmath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexp\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m-\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     18\u001b[0m     \u001b[1;31m### END CODE HERE ###\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m     19\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: bad operand type for unary -: 'list'"
     ]
    }
   ],
   "source": [
    "### One reason why we use \"numpy\" instead of \"math\" in Deep Learning ###\n",
    "x = [1, 2, 3]\n",
    "# 当你运行它给出一个错误，因为X是一个矢量\n",
    "basic_sigmoid(x) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其实，如果$ x = (x_1, x_2, ..., x_n)$ 是一个行向量，$np.exp(x)$ 将会将指数功能运用于x的每一个元素。输出将会是: $np.exp(x) = (e^{x_1}, e^{x_2}, ..., e^{x_n})$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[  2.71828183   7.3890561   20.08553692]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# example of np.exp\n",
    "x = np.array([1, 2, 3])\n",
    "print(np.exp(x)) # result is (exp(1), exp(2), exp(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而且，如果x是一个向量，那么一个诸如 $s = x + 3$ 或者 $s = \\frac{1}{x}$的Python操作将输出一个和x相同大小的向量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4 5 6]\n"
     ]
    }
   ],
   "source": [
    "# example of vector operation\n",
    "x = np.array([1, 2, 3])\n",
    "print (x + 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "任何时候，当你需要查询更多的numpy函数信息，我们都建议你去看[官方文档](https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.exp.html). \n",
    "\n",
    "您也可以在 notebook 中创建一个新的单元格，通过编写`np.exp?`（例如）快速访问文档。\n",
    "\n",
    "**练习**：使用numpy实现sigmoid函数。\n",
    "\n",
    "**说明**:：x现在可以是实数，矢量或矩阵。我们用numpy来表示这些形状（向量，矩阵...）的数据结构被称为numpy数组。关于它你现在无需知道更多。\n",
    "\n",
    "$$ \\text{For } x \\in \\mathbb{R}^n \\text{,     } sigmoid(x) = sigmoid\\begin{pmatrix}\n",
    "    x_1  \\\\\n",
    "    x_2  \\\\\n",
    "    ...  \\\\\n",
    "    x_n  \\\\\n",
    "\\end{pmatrix} = \\begin{pmatrix}\n",
    "    \\frac{1}{1+e^{-x_1}}  \\\\\n",
    "    \\frac{1}{1+e^{-x_2}}  \\\\\n",
    "    ...  \\\\\n",
    "    \\frac{1}{1+e^{-x_n}}  \\\\\n",
    "\\end{pmatrix}\\tag{1} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: sigmoid\n",
    "\n",
    "import numpy as np # this means you can access numpy functions by writing np.function() instead of numpy.function()\n",
    "\n",
    "def sigmoid(x):\n",
    "    \"\"\"\n",
    "    Compute the sigmoid of x\n",
    "\n",
    "    Arguments:\n",
    "    x -- A scalar or numpy array of any size\n",
    "\n",
    "    Return:\n",
    "    s -- sigmoid(x)\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 1 line of code)\n",
    "    s = 1 / (1 + np.exp(-x))\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.73105858,  0.88079708,  0.95257413])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.array([1, 2, 3])\n",
    "sigmoid(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "<table>\n",
    "    <tr> \n",
    "        <td> **sigmoid([1,2,3])**</td> \n",
    "        <td> array([ 0.73105858,  0.88079708,  0.95257413]) </td> \n",
    "    </tr>\n",
    "</table> \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 - Sigmoid gradient（梯度）\n",
    "\n",
    "正如在课堂上学到的，反向传播优化损失函数需要去计算梯度。现在让我们来编写第一个梯度函数。\n",
    "\n",
    "**练习**：完成sigmoid的梯度函数，用它去计算sigmoid相对于其输入x的梯度。公式是：\n",
    "$$sigmoid\\_derivative(x) = \\sigma'(x) = \\sigma(x) (1 - \\sigma(x))\\tag{2}$$\n",
    "\n",
    "你通常会花两步去编写这个函数：\n",
    "1. Set s to be the sigmoid of x. You might find your sigmoid(x) function useful.\n",
    "2. Compute $\\sigma'(x) = s(1-s)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: sigmoid_derivative\n",
    "\n",
    "def sigmoid_derivative(x):\n",
    "    \"\"\"\n",
    "    计算sigmoid function函数相对于其输入x的梯度（也称为斜率或者导数）.\n",
    "    你可以将sigmoid function函数的输出存储到变量中，然后用它来计算梯度\n",
    "    \n",
    "    Arguments:\n",
    "    x -- 一个标量或者numpy数组\n",
    "\n",
    "    Return:\n",
    "    ds -- 你计算好的梯度\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 2 lines of code)\n",
    "    s = sigmoid(x)\n",
    "    ds = s * (1 - s)\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return ds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sigmoid_derivative(x) = [ 0.19661193  0.10499359  0.04517666]\n"
     ]
    }
   ],
   "source": [
    "x = np.array([1, 2, 3])\n",
    "print (\"sigmoid_derivative(x) = \" + str(sigmoid_derivative(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "\n",
    "\n",
    "<table>\n",
    "    <tr> \n",
    "        <td> **sigmoid_derivative([1,2,3])**</td> \n",
    "        <td> [ 0.19661193  0.10499359  0.04517666] </td> \n",
    "    </tr>\n",
    "</table> \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 - Reshaping arrays ###\n",
    "\n",
    "在深度学习中有两个numpy函数经常使用：[np.shape](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.shape.html) 和 [np.reshape()](https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html). \n",
    "- X.shape用于获取矩阵/向量X的形状（维度）。\n",
    "- X.reshape(...)用于将X重塑为其他维度。\n",
    "\n",
    "例如，在计算机科学中，图像是由三维形状数组来表示的$(length, height, depth = 3)$。但是，当你读取图像作为算法的输入时，需要将其转换为形状的矢量 $(length*height*3, 1)$。换句话说，就是你要将3D数组“展开”或重塑造成一维矢量。\n",
    "\n",
    "<img src=\"images/image2vector_kiank.png\" style=\"width:500px;height:300;\">\n",
    "\n",
    "**Exercise**: Implement `image2vector()` that takes an input of shape (length, height, 3) and returns a vector of shape (length\\*height\\*3, 1). For example, if you would like to reshape an array v of shape (a, b, c) into a vector of shape (a*b,c) you would do:\n",
    "``` python\n",
    "v = v.reshape((v.shape[0]*v.shape[1], v.shape[2])) # v.shape[0] = a ; v.shape[1] = b ; v.shape[2] = c\n",
    "```\n",
    "- 请不要将图像的尺寸硬编码为常量。 Instead look up the quantities you need with `image.shape[0]`, etc. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: image2vector\n",
    "def image2vector(image):\n",
    "    \"\"\"\n",
    "    Argument:\n",
    "    image -- a numpy array of shape (length, height, depth)\n",
    "    \n",
    "    Returns:\n",
    "    v -- a vector of shape (length*height*depth, 1)\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 1 line of code)\n",
    "    v = image.reshape(image.shape[0] * image.shape[1] * image.shape[2], 1)\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image2vector(image) = [[ 0.67826139]\n",
      " [ 0.29380381]\n",
      " [ 0.90714982]\n",
      " [ 0.52835647]\n",
      " [ 0.4215251 ]\n",
      " [ 0.45017551]\n",
      " [ 0.92814219]\n",
      " [ 0.96677647]\n",
      " [ 0.85304703]\n",
      " [ 0.52351845]\n",
      " [ 0.19981397]\n",
      " [ 0.27417313]\n",
      " [ 0.60659855]\n",
      " [ 0.00533165]\n",
      " [ 0.10820313]\n",
      " [ 0.49978937]\n",
      " [ 0.34144279]\n",
      " [ 0.94630077]]\n"
     ]
    }
   ],
   "source": [
    "# This is a 3 by 3 by 2 array, typically images will be (num_px_x, num_px_y,3) where 3 represents the RGB values\n",
    "image = np.array([[[ 0.67826139,  0.29380381],\n",
    "        [ 0.90714982,  0.52835647],\n",
    "        [ 0.4215251 ,  0.45017551]],\n",
    "\n",
    "       [[ 0.92814219,  0.96677647],\n",
    "        [ 0.85304703,  0.52351845],\n",
    "        [ 0.19981397,  0.27417313]],\n",
    "\n",
    "       [[ 0.60659855,  0.00533165],\n",
    "        [ 0.10820313,  0.49978937],\n",
    "        [ 0.34144279,  0.94630077]]])\n",
    "\n",
    "print (\"image2vector(image) = \" + str(image2vector(image)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "\n",
    "\n",
    "<table style=\"width:100%\">\n",
    "     <tr> \n",
    "       <td> **image2vector(image)** </td> \n",
    "       <td> [[ 0.67826139]\n",
    " [ 0.29380381]\n",
    " [ 0.90714982]\n",
    " [ 0.52835647]\n",
    " [ 0.4215251 ]\n",
    " [ 0.45017551]\n",
    " [ 0.92814219]\n",
    " [ 0.96677647]\n",
    " [ 0.85304703]\n",
    " [ 0.52351845]\n",
    " [ 0.19981397]\n",
    " [ 0.27417313]\n",
    " [ 0.60659855]\n",
    " [ 0.00533165]\n",
    " [ 0.10820313]\n",
    " [ 0.49978937]\n",
    " [ 0.34144279]\n",
    " [ 0.94630077]]</td> \n",
    "     </tr>\n",
    "    \n",
    "   \n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 - Normalizing rows\n",
    "\n",
    "我们在机器学习和深度学习中常用的另一个技术是正则化我们的数据. 它经常产生更好的性能，因为梯度下降在正则化之后收敛得更快。 在这里，正则化的意思是把x改成 $ \\frac{x}{\\| x\\|} $ (dividing each row vector of x by its norm).\n",
    "\n",
    "For example, if $$x = \n",
    "\\begin{bmatrix}\n",
    "    0 & 3 & 4 \\\\\n",
    "    2 & 6 & 4 \\\\\n",
    "\\end{bmatrix}\\tag{3}$$ then $$\\| x\\| = np.linalg.norm(x, axis = 1, keepdims = True) = \\begin{bmatrix}\n",
    "    5 \\\\\n",
    "    \\sqrt{56} \\\\\n",
    "\\end{bmatrix}\\tag{4} $$and        $$ x\\_normalized = \\frac{x}{\\| x\\|} = \\begin{bmatrix}\n",
    "    0 & \\frac{3}{5} & \\frac{4}{5} \\\\\n",
    "    \\frac{2}{\\sqrt{56}} & \\frac{6}{\\sqrt{56}} & \\frac{4}{\\sqrt{56}} \\\\\n",
    "\\end{bmatrix}\\tag{5}$$ Note that you can divide matrices of different sizes and it works fine: this is called broadcasting and you're going to learn about it in part 5.\n",
    "\n",
    "\n",
    "**Exercise**: Implement normalizeRows() to normalize the rows of a matrix. After applying this function to an input matrix x, each row of x should be a vector of unit length (meaning length 1)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: normalizeRows\n",
    "\n",
    "def normalizeRows(x):\n",
    "    \"\"\"\n",
    "    Implement a function that normalizes each row of the matrix x (to have unit length).\n",
    "    \n",
    "    Argument:\n",
    "    x -- A numpy matrix of shape (n, m)\n",
    "    \n",
    "    Returns:\n",
    "    x -- The normalized (by row) numpy matrix. You are allowed to modify x.\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 2 lines of code)\n",
    "    # Compute x_norm as the norm 2 of x. Use np.linalg.norm(..., ord = 2, axis = ..., keepdims = True)\n",
    "    x_norm = np.linalg.norm(x, axis = 1, keepdims = True)\n",
    "    \n",
    "    # Divide x by its norm.\n",
    "    x = x / x_norm\n",
    "    ### END CODE HERE ###\n",
    "\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "normalizeRows(x) = [[ 0.          0.6         0.8       ]\n",
      " [ 0.13736056  0.82416338  0.54944226]]\n"
     ]
    }
   ],
   "source": [
    "x = np.array([\n",
    "    [0, 3, 4],\n",
    "    [1, 6, 4]])\n",
    "print(\"normalizeRows(x) = \" + str(normalizeRows(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "\n",
    "<table style=\"width:60%\">\n",
    "\n",
    "     <tr> \n",
    "       <td> **normalizeRows(x)** </td> \n",
    "       <td> [[ 0.          0.6         0.8       ]\n",
    " [ 0.13736056  0.82416338  0.54944226]]</td> \n",
    "     </tr>\n",
    "    \n",
    "   \n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**:\n",
    "In normalizeRows(), you can try to print the shapes of x_norm and x, and then rerun the assessment. You'll find out that they have different shapes. This is normal given that x_norm takes the norm of each row of x. So x_norm has the same number of rows but only 1 column. So how did it work when you divided x by x_norm? This is called broadcasting and we'll talk about it now! "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.5 - Broadcasting and the softmax function ####\n",
    "在numpy中有一个很重要的概念叫“广播”。它在不同形状的数组之间进行数学运算是非常有用的。有关广播的全部细节，您可以阅读官方的[broadcasting documentation](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**练习**：使用numpy实现softmax函数。当你需要对两个或更多的类进行分类时，您可以将softmax视为normalizing function。您将在本专业的第二课程中学习更多有关softmax的知识。\n",
    "\n",
    "**Instructions**:\n",
    "- $ \\text{for } x \\in \\mathbb{R}^{1\\times n} \\text{,     } softmax(x) = softmax(\\begin{bmatrix}\n",
    "    x_1  &&\n",
    "    x_2 &&\n",
    "    ...  &&\n",
    "    x_n  \n",
    "\\end{bmatrix}) = \\begin{bmatrix}\n",
    "     \\frac{e^{x_1}}{\\sum_{j}e^{x_j}}  &&\n",
    "    \\frac{e^{x_2}}{\\sum_{j}e^{x_j}}  &&\n",
    "    ...  &&\n",
    "    \\frac{e^{x_n}}{\\sum_{j}e^{x_j}} \n",
    "\\end{bmatrix} $ \n",
    "\n",
    "- $\\text{for a matrix } x \\in \\mathbb{R}^{m \\times n} \\text{,  $x_{ij}$ maps to the element in the $i^{th}$ row and $j^{th}$ column of $x$, thus we have: }$  $$softmax(x) = softmax\\begin{bmatrix}\n",
    "    x_{11} & x_{12} & x_{13} & \\dots  & x_{1n} \\\\\n",
    "    x_{21} & x_{22} & x_{23} & \\dots  & x_{2n} \\\\\n",
    "    \\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    "    x_{m1} & x_{m2} & x_{m3} & \\dots  & x_{mn}\n",
    "\\end{bmatrix} = \\begin{bmatrix}\n",
    "    \\frac{e^{x_{11}}}{\\sum_{j}e^{x_{1j}}} & \\frac{e^{x_{12}}}{\\sum_{j}e^{x_{1j}}} & \\frac{e^{x_{13}}}{\\sum_{j}e^{x_{1j}}} & \\dots  & \\frac{e^{x_{1n}}}{\\sum_{j}e^{x_{1j}}} \\\\\n",
    "    \\frac{e^{x_{21}}}{\\sum_{j}e^{x_{2j}}} & \\frac{e^{x_{22}}}{\\sum_{j}e^{x_{2j}}} & \\frac{e^{x_{23}}}{\\sum_{j}e^{x_{2j}}} & \\dots  & \\frac{e^{x_{2n}}}{\\sum_{j}e^{x_{2j}}} \\\\\n",
    "    \\vdots & \\vdots & \\vdots & \\ddots & \\vdots \\\\\n",
    "    \\frac{e^{x_{m1}}}{\\sum_{j}e^{x_{mj}}} & \\frac{e^{x_{m2}}}{\\sum_{j}e^{x_{mj}}} & \\frac{e^{x_{m3}}}{\\sum_{j}e^{x_{mj}}} & \\dots  & \\frac{e^{x_{mn}}}{\\sum_{j}e^{x_{mj}}}\n",
    "\\end{bmatrix} = \\begin{pmatrix}\n",
    "    softmax\\text{(first row of x)}  \\\\\n",
    "    softmax\\text{(second row of x)} \\\\\n",
    "    ...  \\\\\n",
    "    softmax\\text{(last row of x)} \\\\\n",
    "\\end{pmatrix} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: softmax\n",
    "\n",
    "def softmax(x):\n",
    "    \"\"\"Calculates the softmax for each row of the input x.\n",
    "\n",
    "    Your code should work for a row vector and also for matrices of shape (n, m).\n",
    "\n",
    "    Argument:\n",
    "    x -- A numpy matrix of shape (n,m)\n",
    "\n",
    "    Returns:\n",
    "    s -- A numpy matrix equal to the softmax of x, of shape (n,m)\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 3 lines of code)\n",
    "    # Apply exp() element-wise to x. Use np.exp(...).\n",
    "    x_exp = np.exp(x)\n",
    "\n",
    "    # Create a vector x_sum that sums each row of x_exp. Use np.sum(..., axis = 1, keepdims = True).\n",
    "    x_sum = np.sum(x_exp, axis = 1, keepdims = True)\n",
    "    \n",
    "    # Compute softmax(x) by dividing x_exp by x_sum. It should automatically use numpy broadcasting.\n",
    "    s = x_exp / x_sum\n",
    "\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "softmax(x) = [[  9.80897665e-01   8.94462891e-04   1.79657674e-02   1.21052389e-04\n",
      "    1.21052389e-04]\n",
      " [  8.78679856e-01   1.18916387e-01   8.01252314e-04   8.01252314e-04\n",
      "    8.01252314e-04]]\n"
     ]
    }
   ],
   "source": [
    "x = np.array([\n",
    "    [9, 2, 5, 0, 0],\n",
    "    [7, 5, 0, 0 ,0]])\n",
    "print(\"softmax(x) = \" + str(softmax(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**:\n",
    "\n",
    "<table style=\"width:60%\">\n",
    "\n",
    "     <tr> \n",
    "       <td> **softmax(x)** </td> \n",
    "       <td> [[  9.80897665e-01   8.94462891e-04   1.79657674e-02   1.21052389e-04\n",
    "    1.21052389e-04]\n",
    " [  8.78679856e-01   1.18916387e-01   8.01252314e-04   8.01252314e-04\n",
    "    8.01252314e-04]]</td> \n",
    "     </tr>\n",
    "</table>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**:\n",
    "- If you print the shapes of x_exp, x_sum and s above and rerun the assessment cell, you will see that x_sum is of shape (2,1) while x_exp and s are of shape (2,5). **x_exp/x_sum** works due to python broadcasting.\n",
    "\n",
    "恭喜！你现在已经对python numpy有了很好的理解，并且已经实现了一些有用的功能，你将会在深度学习中使用它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='blue'>\n",
    "**你需要记住的是：**\n",
    "- np.exp(x) works for any np.array x and applies the exponential function to every coordinate（坐标）\n",
    "- sigmoid函数及其梯度\n",
    "- image2vector常用于深度学习\n",
    "- np.reshape被广泛使用。将来，你会发现保持矩阵/矢量的维度直接将消除大量的错误。\n",
    "- numpy具有高效的内置功能\n",
    "- 广播非常有用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 2) Vectorization（矢量化）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在深度学习中，需要处理非常大的数据集。为了确保计算高效，数据需要向量化。 For example, try to tell the difference between the following implementations of the dot/outer/elementwise product."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dot = 278\n",
      " ----- Computation time = 0.0ms\n",
      "outer = [[ 81.  18.  18.  81.   0.  81.  18.  45.   0.   0.  81.  18.  45.   0.\n",
      "    0.]\n",
      " [ 18.   4.   4.  18.   0.  18.   4.  10.   0.   0.  18.   4.  10.   0.\n",
      "    0.]\n",
      " [ 45.  10.  10.  45.   0.  45.  10.  25.   0.   0.  45.  10.  25.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [ 63.  14.  14.  63.   0.  63.  14.  35.   0.   0.  63.  14.  35.   0.\n",
      "    0.]\n",
      " [ 45.  10.  10.  45.   0.  45.  10.  25.   0.   0.  45.  10.  25.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [ 81.  18.  18.  81.   0.  81.  18.  45.   0.   0.  81.  18.  45.   0.\n",
      "    0.]\n",
      " [ 18.   4.   4.  18.   0.  18.   4.  10.   0.   0.  18.   4.  10.   0.\n",
      "    0.]\n",
      " [ 45.  10.  10.  45.   0.  45.  10.  25.   0.   0.  45.  10.  25.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]\n",
      " [  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.\n",
      "    0.]]\n",
      " ----- Computation time = 0.0ms\n",
      "elementwise multiplication = [ 81.   4.  10.   0.   0.  63.  10.   0.   0.   0.  81.   4.  25.   0.   0.]\n",
      " ----- Computation time = 0.0ms\n",
      "gdot = [ 23.20908084  21.98367446  19.66736975]\n",
      " ----- Computation time = 0.0ms\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]\n",
    "x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]\n",
    "\n",
    "### CLASSIC DOT PRODUCT OF VECTORS IMPLEMENTATION ###\n",
    "tic = time.process_time()\n",
    "dot = 0\n",
    "for i in range(len(x1)):\n",
    "    dot+= x1[i]*x2[i]\n",
    "toc = time.process_time()\n",
    "print (\"dot = \" + str(dot) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### CLASSIC OUTER PRODUCT IMPLEMENTATION ###\n",
    "tic = time.process_time()\n",
    "outer = np.zeros((len(x1),len(x2))) # we create a len(x1)*len(x2) matrix with only zeros\n",
    "for i in range(len(x1)):\n",
    "    for j in range(len(x2)):\n",
    "        outer[i,j] = x1[i]*x2[j]\n",
    "toc = time.process_time()\n",
    "print (\"outer = \" + str(outer) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### CLASSIC ELEMENTWISE IMPLEMENTATION ###\n",
    "tic = time.process_time()\n",
    "mul = np.zeros(len(x1))\n",
    "for i in range(len(x1)):\n",
    "    mul[i] = x1[i]*x2[i]\n",
    "toc = time.process_time()\n",
    "print (\"elementwise multiplication = \" + str(mul) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### CLASSIC GENERAL DOT PRODUCT IMPLEMENTATION ###\n",
    "W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array\n",
    "tic = time.process_time()\n",
    "gdot = np.zeros(W.shape[0])\n",
    "for i in range(W.shape[0]):\n",
    "    for j in range(len(x1)):\n",
    "        gdot[i] += W[i,j]*x1[j]\n",
    "toc = time.process_time()\n",
    "print (\"gdot = \" + str(gdot) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dot = 278\n",
      " ----- Computation time = 0.0ms\n",
      "outer = [[81 18 18 81  0 81 18 45  0  0 81 18 45  0  0]\n",
      " [18  4  4 18  0 18  4 10  0  0 18  4 10  0  0]\n",
      " [45 10 10 45  0 45 10 25  0  0 45 10 25  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [63 14 14 63  0 63 14 35  0  0 63 14 35  0  0]\n",
      " [45 10 10 45  0 45 10 25  0  0 45 10 25  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [81 18 18 81  0 81 18 45  0  0 81 18 45  0  0]\n",
      " [18  4  4 18  0 18  4 10  0  0 18  4 10  0  0]\n",
      " [45 10 10 45  0 45 10 25  0  0 45 10 25  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\n",
      " [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]]\n",
      " ----- Computation time = 0.0ms\n",
      "elementwise multiplication = [81  4 10  0  0 63 10  0  0  0 81  4 25  0  0]\n",
      " ----- Computation time = 0.0ms\n",
      "gdot = [ 23.20908084  21.98367446  19.66736975]\n",
      " ----- Computation time = 15.625ms\n"
     ]
    }
   ],
   "source": [
    "x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]\n",
    "x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]\n",
    "\n",
    "### VECTORIZED DOT PRODUCT OF VECTORS ###\n",
    "tic = time.process_time()\n",
    "dot = np.dot(x1,x2)\n",
    "toc = time.process_time()\n",
    "print (\"dot = \" + str(dot) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### VECTORIZED OUTER PRODUCT ###\n",
    "tic = time.process_time()\n",
    "outer = np.outer(x1,x2)\n",
    "toc = time.process_time()\n",
    "print (\"outer = \" + str(outer) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### VECTORIZED ELEMENTWISE MULTIPLICATION ###\n",
    "tic = time.process_time()\n",
    "mul = np.multiply(x1,x2)\n",
    "toc = time.process_time()\n",
    "print (\"elementwise multiplication = \" + str(mul) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")\n",
    "\n",
    "### VECTORIZED GENERAL DOT PRODUCT ###\n",
    "tic = time.process_time()\n",
    "dot = np.dot(W,x1)\n",
    "toc = time.process_time()\n",
    "print (\"gdot = \" + str(dot) + \"\\n ----- Computation time = \" + str(1000*(toc - tic)) + \"ms\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "正如你看到的那样，矢量化的实现更简洁更高效。对于更大的向量/矩阵，运行时间的差异会变得更大。\n",
    "\n",
    "**Note** that `np.dot()` performs a matrix-matrix or matrix-vector multiplication. This is different from `np.multiply()` and the `*` operator (which is equivalent to  `.*` in Matlab/Octave), which performs an element-wise multiplication."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Implement the L1 and L2 loss functions\n",
    "\n",
    "**Exercise**: Implement the numpy vectorized version of the L1 loss. You may find the function abs(x) (absolute value of x) useful.\n",
    "\n",
    "**Reminder**:\n",
    "- 成本函数用于评估模型的性能。你的预测值($ \\hat{y} $)和真实值($y$)相差越大，你的成本函数就越大。在深度学习中，您可以使用梯度下降等优化算法来训练模型，并将成本降至最低。\n",
    "- L1 loss is defined as:\n",
    "$$\\begin{align*} & L_1(\\hat{y}, y) = \\sum_{i=0}^m|y^{(i)} - \\hat{y}^{(i)}| \\end{align*}\\tag{6}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: L1\n",
    "\n",
    "def L1(yhat, y):\n",
    "    \"\"\"\n",
    "    Arguments:\n",
    "    yhat -- vector of size m (predicted labels)\n",
    "    y -- vector of size m (true labels)\n",
    "    \n",
    "    Returns:\n",
    "    loss -- the value of the L1 loss function defined above\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 1 line of code)\n",
    "    loss = np.sum(np.abs(y - yhat))\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "L1 = 1.1\n"
     ]
    }
   ],
   "source": [
    "yhat = np.array([.9, 0.2, 0.1, .4, .9])\n",
    "y = np.array([1, 0, 0, 1, 1])\n",
    "print(\"L1 = \" + str(L1(yhat,y)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**:\n",
    "\n",
    "<table style=\"width:20%\">\n",
    "\n",
    "     <tr> \n",
    "       <td> **L1** </td> \n",
    "       <td> 1.1 </td> \n",
    "     </tr>\n",
    "</table>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise**: 实现L2损失的numpy向量化版本。实现L2损失有好几种方法，但你可能发现函数 np.dot() 更好用. As a reminder, if $x = [x_1, x_2, ..., x_n]$, then `np.dot(x,x)` = $\\sum_{j=0}^n x_j^{2}$. \n",
    "\n",
    "\n",
    "\n",
    "- L2 loss is defined as $$\\begin{align*} & L_2(\\hat{y},y) = \\sum_{i=0}^m(y^{(i)} - \\hat{y}^{(i)})^2 \\end{align*}\\tag{7}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: L2\n",
    "\n",
    "def L2(yhat, y):\n",
    "    \"\"\"\n",
    "    Arguments:\n",
    "    yhat -- vector of size m (predicted labels)\n",
    "    y -- vector of size m (true labels)\n",
    "    \n",
    "    Returns:\n",
    "    loss -- the value of the L2 loss function defined above\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ### (≈ 1 line of code)\n",
    "    loss = np.dot((y - yhat),(y - yhat).T)\n",
    "    ### END CODE HERE ###\n",
    "    \n",
    "    return loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "L2 = 0.43\n"
     ]
    }
   ],
   "source": [
    "yhat = np.array([.9, 0.2, 0.1, .4, .9])\n",
    "y = np.array([1, 0, 0, 1, 1])\n",
    "print(\"L2 = \" + str(L2(yhat,y)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output**: \n",
    "<table style=\"width:20%\">\n",
    "     <tr> \n",
    "       <td> **L2** </td> \n",
    "       <td> 0.43 </td> \n",
    "     </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "祝贺您完成这项任务。我们希望这个小小的热身练习可以在未来的任务中帮助你。未来的任务更加精彩和有趣！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='blue'>\n",
    "**需要记住的是：**\n",
    "\n",
    "- 矢量化在深度学习中非常重要。它提供了计算效率和清晰度（clarity）。\n",
    "- 你已经复习了L1和L2损失。\n",
    "- 您熟悉了 np.sum，np.dot，np.multiply，np.maximum 等许多numpy函数。"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "coursera": {
   "course_slug": "neural-networks-deep-learning",
   "graded_item_id": "XHpfv",
   "launcher_item_id": "Zh0CU"
  },
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
